/**
 * @typedef {[number, number, number]} heatmapPoint
 */

import * as THREE from 'three';

/**
 * - 三维数据
 *  - X,Y为坐标，Z为热力值
 *  - 原点在左下角
 */

const defaultData = [
  [0, 0, 5],
  [0.5, 0.5, 10],
  [0.25, 0.25, 9.5],
  [1, 1, 7],
];
const defaultLength = 100;
/**
 *
 * @param {object} option
 * @param {number} option.width
 * @param {number} option.height
 * @param {Array<heatmapPoint>} option.data
 * @returns {THREE.Mesh}
 */
export function createHeatMapMesh(option = {}) {
  const { width = defaultLength, height = defaultLength, data = defaultData } = option;
  const arr = data.map((i) => new THREE.Vector3(...i));

  const uniforms1 = {
    HEAT_MAX: { value: 10, type: 'number', unit: 'float' },
    PointRadius: { value: 0.42, type: 'number', unit: 'float' },
    PointsCount: { value: arr.length, type: 'number-array', unit: 'int' }, // 数量
    // 低热量区域
    c1: { value: new THREE.Color(0x000000), type: 'color', unit: 'vec3' },
    // 高热量区域
    c2: { value: new THREE.Color(0x000000), type: 'color', unit: 'vec3' },
    uvY: { value: 1, type: 'number', unit: 'float' },
    uvX: { value: 1, type: 'number', unit: 'float' },
    opacity: { value: 1, type: 'number', unit: 'float' },
  };

  const uniforms2 = {
    Points: { value: arr, type: 'vec3-array', unit: 'vec3' },
  };

  const uniforms = {
    ...uniforms1,
    ...uniforms2,
  };

  const vertexShader = `
    varying vec2 vUv;
    void main() {
        vUv = uv;
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `;

  const getFragmentShader = () =>
    'precision highp float;\n' +
    'varying vec2 vUv; \n' +
    Object.keys(uniforms1)
      .map((i) => 'uniform ' + uniforms1[i].unit + ' ' + i + ';')
      .join('\n') +
    '\nuniform vec3 Points[' +
    uniforms1.PointsCount.value +
    '];' +
    `
      vec3 gradient(float w, vec2 uv) {
          w = pow(clamp(w, 0., 1.) * 3.14159 * .5, .9);
          return vec3(sin(w), sin(w * 2.), cos(w))* 1.1 + mix(c1, c2, w) * 1.1; 
      }
      void main()
      {
          vec2 uv = vUv;
          uv.xy *= vec2(uvX, uvY);
          float d = 0.;
          for (int i = 0; i < PointsCount; i++) {
              vec3 v = Points[i];
              float intensity = v.z / HEAT_MAX;
              float pd = (1. - length(uv - v.xy) / PointRadius) * intensity;
              d += pow(max(0., pd), 2.);
          }
        // 使用指定透明度
        // gl_FragColor = vec4(gradient(d, uv), opacity);
          
        // 当热力值为0时,直接设置透明
        float alpha = clamp(d * opacity, 0.0, 1.0);
        gl_FragColor = vec4(gradient(d, uv), alpha);
      } `;

  const shaderMaterial = new THREE.ShaderMaterial({
    uniforms,
    side: THREE.DoubleSide, // 确保两面可见
    vertexShader,
    fragmentShader: getFragmentShader(),
    transparent: true,
  });

  const planeGeometry = new THREE.PlaneGeometry(width, height);
  const heatMesh = new THREE.Mesh(planeGeometry, shaderMaterial);
  return heatMesh;
}

// 内发光灯泡模拟
// export function createSelfLightMaterial() {
//   const vertexShader = `
//   varying vec3 vNormal;
//   varying vec3 vPositionNormal;

//   void main() {
//     // 视图空间下的单位法线向量
//     vNormal = normalize(normalMatrix * normal);
//     // 视图空间下的单位坐标向量
//     vPositionNormal = normalize((modelViewMatrix * vec4(position, 1.0) ).xyz);
//       gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
//   }
//   `;

//   /**
//    * 需要注意法线向量值，菲涅尔反射对于法线向量差别比较大的模型效果明显，即不规则物体
//    * 模型表面平整的话效果较差，不过可以使用法线贴图更改物体法线向量
//    */
//   const fragmentShader = `
//   uniform vec3 uColor;
//   uniform float uBias;
//   uniform float uPower;
//   uniform float uScale;

//   varying vec3 vNormal;
//   varying vec3 vPositionNormal;

//   // 菲涅尔反射
//   float fresnelReflex() {
//       return pow(uBias + uScale * abs(dot(vNormal, vPositionNormal)), uPower);
//   }

//   void main() {
//       float opacity = fresnelReflex();
//       gl_FragColor = vec4(uColor, opacity);
//   }`;

//   const material = new THREE.ShaderMaterial({
//     uniforms: {
//       uColor: { value: new THREE.Color(0x00ffff) },
//       uBias: { value: 1.0 },
//       uScale: { value: -1.0 },
//       uPower: { value: 2.0 },
//     },
//     vertexShader,
//     fragmentShader,
//     transparent: true,
//   });
//   return material;
// }

export function createSkyTextureAndBox(type) {
  let urls = [];
  let textureCube = null;
  switch (type) {
    case 'day':
      {
        urls = [1, 2, 3, 4, 5, 6].map((k) => `3D/sky02/${k}.png`);
        textureCube = new THREE.CubeTextureLoader().load(urls);
      }
      break;
    case 'night':
      {
        urls = [1, 2, 3, 4, 5, 6].map((k) => `3D/sky01/${k}.png`);
        textureCube = new THREE.CubeTextureLoader().load(urls);
      }
      break;
  }

  // const textureCube = new THREE.CubeTextureLoader().load(urls);
  const scale = 500;
  const boxGeometry = new THREE.BoxGeometry(scale, scale, scale);
  const skyboxMaterial = new THREE.MeshStandardMaterial({
    side: THREE.BackSide,
    color: 0xffffff,
    envMap: textureCube,
    metalness: 1,
    roughness: 0,
  });
  const boxMesh = new THREE.Mesh(boxGeometry, skyboxMaterial);
  return {
    boxMesh,
    textureCube,
  };
}

export function createIntermediateSkybox() {
  const urls = [1, 2, 3, 4, 5, 6].map((k) => `3D/sky03/${k}.png`);
  const textureCube = new THREE.CubeTextureLoader().load(urls);
  return { textureCube };
}
